
using System;
using System.ComponentModel;
using System.IO;
using System.Runtime.InteropServices;
using System.Security.Principal;

namespace GPDWin4GameBarPowerBackend {
	public class ProcessAsUser
	{
		

		private enum WTS_CONNECTSTATE_CLASS
		{
			WTSActive,
			WTSConnected,
			WTSConnectQuery,
			WTSShadow,
			WTSDisconnected,
			WTSIdle,
			WTSListen,
			WTSReset,
			WTSDown,
			WTSInit
		}

		public struct SECURITY_ATTRIBUTES
		{
			public uint nLength;

			public uint lpSecurityDescriptor;

			public bool bInheritHandle;
		}

		public struct STARTUPINFO
		{
			public uint cb;

			public string lpReserved;

			public string lpDesktop;

			public string lpTitle;

			public uint dwX;

			public uint dwY;

			public uint dwXSize;

			public uint dwYSize;

			public uint dwXCountChars;

			public uint dwYCountChars;

			public uint dwFillAttribute;

			public uint dwFlags;

			public ushort wShowWindow;

			public ushort cbReserved2;

			public IntPtr lpReserved2;

			public IntPtr hStdInput;

			public IntPtr hStdOutput;

			public IntPtr hStdError;
		}

		public struct PROCESS_INFORMATION
		{
			public IntPtr hProcess;

			public IntPtr hThread;

			public uint dwProcessId;

			public uint dwThreadId;
		}

		

		public const int GENERIC_ALL_ACCESS = 268435456;

		
		
		[DllImport("kernel32.dll")]
		private static extern uint WTSGetActiveConsoleSessionId();

		

		[DllImport("kernel32.dll")]
		private static extern bool CloseHandle(IntPtr hSnapshot);

		[DllImport("advapi32.dll")]
		private static extern bool CreateProcessAsUser(IntPtr hToken, string lpApplicationName, string lpCommandLine, ref SECURITY_ATTRIBUTES lpProcessAttributes, ref SECURITY_ATTRIBUTES lpThreadAttributes, bool bInheritHandle, uint dwCreationFlags, uint lpEnvironment, string lpCurrentDirectory, ref STARTUPINFO lpStartupInfo, out PROCESS_INFORMATION lpProcessInformation);
        [DllImport("advapi32.dll", SetLastError = true)]
        public static extern bool OpenProcessToken(IntPtr ProcessHandle, int DesiredAccess, out IntPtr TokenHandle);
        [DllImport("kernel32.dll", SetLastError = true)]
        public static extern IntPtr GetCurrentProcess();
        
        [DllImport("Userenv.dll", SetLastError = true)]
        public static extern bool DestroyEnvironmentBlock(uint lpEnvironment);
        [DllImport("advapi32.dll", SetLastError = true)]
        public static extern bool SetTokenInformation(IntPtr TokenHandle, int TokenInformationClass, out uint TokenInformation, int TokenInformationLength);
        
		[DllImport("advapi32.dll", SetLastError = true)]
		public static extern bool DuplicateTokenEx(IntPtr hExistingToken, int dwDesiredAccess, ref SECURITY_ATTRIBUTES lpThreadAttributes, int ImpersonationLevel, int dwTokenType, ref IntPtr phNewToken);

		

		[DllImport("userenv.dll", SetLastError = true)]
		private static extern bool CreateEnvironmentBlock(out uint lpEnvironment, IntPtr hToken, bool bInherit);

		
        public static void Start(string commandLine, bool showWindow)
        {
            IntPtr hToken;
            IntPtr hTokenDup = IntPtr.Zero;
            const int TOKEN_ALL_ACCESS = 268435456;
            const int TokenSessionId = 12;
            const uint CREATE_PROCESS_FLAGS = 0x00000020 | 0x00000010 | 0x400;

            if (!OpenProcessToken(GetCurrentProcess(), TOKEN_ALL_ACCESS, out hToken))
            {
                throw new Win32Exception(Marshal.GetLastWin32Error());
            }

            var sa = new SECURITY_ATTRIBUTES();
            sa.bInheritHandle = true;
            sa.nLength = (uint)Marshal.SizeOf(sa);

            if (!DuplicateTokenEx(hToken, 268435456, ref sa, 1, 1, ref hTokenDup))
            {
                var error = Marshal.GetLastWin32Error();
                CloseHandle(hToken);
                throw new Win32Exception(error);
            }

            var si = new STARTUPINFO();
            si.cb = (uint)Marshal.SizeOf(si);
            si.lpDesktop = "WinSta0\\Default";
            if (!showWindow)
            {
                si.dwFlags = 1;//STARTF
                si.wShowWindow = 0;
            }
            uint pEnv;
            var dwSessionId = WTSGetActiveConsoleSessionId();

            if (!SetTokenInformation(hTokenDup, TokenSessionId, out dwSessionId, sizeof(uint)))
            {
                var error = Marshal.GetLastWin32Error();
                CloseHandle(hToken);
                CloseHandle(hTokenDup);
                throw new Win32Exception(error);
            }

            if (!CreateEnvironmentBlock(out pEnv, hTokenDup, false))
            {
                var error = Marshal.GetLastWin32Error();
                CloseHandle(hToken);
                CloseHandle(hTokenDup);
                throw new Win32Exception(error);
            }

            PROCESS_INFORMATION pro;
            if (!CreateProcessAsUser(hTokenDup, null, commandLine, ref sa, ref sa, true, CREATE_PROCESS_FLAGS, pEnv, null, ref si, out pro))
            {
                var error = Marshal.GetLastWin32Error();
                CloseHandle(hToken);
                CloseHandle(hTokenDup);
                throw new Win32Exception(error);
            }

            if (pEnv != 0)
            {
                DestroyEnvironmentBlock(pEnv);
            }

            CloseHandle(hToken);
            CloseHandle(hTokenDup);
        }
        
	}
}
